import cv2
import numpy as np
def detectAndDescribe(image):   #定义函数，检测、计算并绘制特征点
    gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    sift=cv2.SIFT_create()
    kp,des=sift.detectAndCompute(gray,None)     #检测并计算特征点
    image=cv2.drawKeypoints(image,kp,image)     #绘制特征点
    return image,kp,des
img1=cv2.imread("left_02.png")    #读取原图像
img2=cv2.imread("right_02.png")
imgA=img1.copy()                            #备份原图像
imgB=img2.copy()
#调用函数detectAndDescribe(),检测、计算并绘制特征点
imgA,kpA,desA=detectAndDescribe(imgA)
imgB,kpB,desB=detectAndDescribe(imgB)
input=np.hstack((imgA,imgB))    #水平堆叠图像
cv2.imshow('Keypoints_Input',input)     #显示绘制特征点的图像

matcher=cv2.BFMatcher(cv2.NORM_L2)  #创建BFMatcher对象
#进项特征匹配，每个特征点保留两个匹配结果
rawMatches=matcher.knnMatch(desA,desB,2)
ratio=0.7
#当最近距离与次近距离的比值小于ratio时，保留此匹配对
matches=[[m]for m,n in rawMatches if m.distance<ratio*n.distance]
#绘制匹配的特征点
out=cv2.drawMatchesKnn(imgA,kpA,imgB,kpB,matches,None)
cv2.imshow('drawMatches',out)       #显示匹配结果图像

def get_homo(kpA,kpB,matches):      #定义函数，获得透视变换矩阵
    reprojThresh=4.0    #定义
    #讲图像特征点转换成Numpy数组
    kpsA=np.float32([kp.pt for kp in kpA])
    kpsB=np.float32([kp.pt for kp in kpB])
    #获取匹配对的点坐标
    ptsA=np.float32([kpsA[m[0].queryIdx] for m in matches])
    ptsB=np.float32([kpsB[m[0].trainIdx] for m in matches])
    #计算透视变换矩阵
    (H,status)=cv2.findHomography(ptsB,ptsA,cv2.RANSAC,reprojThresh)
    return H
if len(matches)>4:
    H=get_homo(kpA,kpB,matches)
    #利用透视变换矩阵进行透视变换
    result=cv2.warpPerspective(img2,H,(img1.shape[1]+img2.shape[1],img2.shape[0]))
    cv2.imshow('Right',result)
    result[0:img1.shape[0],0:img1.shape[1]]=img1    #拼接图像
    cv2.imshow('Result',result)
cv2.waitKey()
cv2.destroyAllWindows()